<?php

namespace IAWP\Tables;

use IAWP\Campaign_Builder;
use IAWP\Dashboard_Options;
use IAWP\Date_Picker\Date_Picker;
use IAWP\Filters;
use IAWP\Form_Submissions\Form;
use IAWP\Icon_Directory_Factory;
use IAWP\Plugin_Group;
use IAWP\Rows\Filter;
use IAWP\Sort_Configuration;
use IAWP\Statistics\Statistics;
use IAWP\Tables\Columns\Column;
use IAWP\Tables\Groups\Group;
use IAWP\Tables\Groups\Groups;
use IAWP\Utils\CSV;
use IAWP\Utils\Currency;
use IAWP\Utils\Number_Formatter;
use IAWP\Utils\Security;
use IAWP\Utils\Timezone;
use IAWP\Utils\URL;
use IAWP\Utils\WordPress_Site_Date_Format_Pattern;
use IAWPSCOPED\Illuminate\Support\Collection;
use IAWPSCOPED\Illuminate\Support\Str;
/** @internal */
abstract class Table
{
    protected $default_sorting_column = 'visitors';
    private $filters;
    private $visible_columns;
    private $group;
    private $is_new_group;
    /** @var ?Statistics */
    private $statistics;
    /**
     * @param string|null $group_id
     * @param bool $is_new_group
     */
    public function __construct(?string $group_id = null, bool $is_new_group = \false)
    {
        $this->visible_columns = Dashboard_Options::getInstance()->visible_columns();
        $this->group = $this->groups()->find_by_id($group_id);
        $this->is_new_group = $is_new_group;
        $this->filters = new Filters();
    }
    protected abstract function groups() : Groups;
    /**
     * @return array<Column>
     */
    protected abstract function local_columns() : array;
    public abstract function id() : string;
    /**
     * @return string[]
     */
    public function visible_column_ids() : array
    {
        $visible_columns = [];
        foreach ($this->get_columns() as $column) {
            if ($column->is_visible()) {
                $visible_columns[] = $column->id();
            }
        }
        return $visible_columns;
    }
    public function allow_downloading() : bool
    {
        return \true;
    }
    public function group() : Group
    {
        return $this->group;
    }
    public function column_picker_html() : string
    {
        return \IAWPSCOPED\iawp_blade()->run('plugin-group-options', ['option_type' => 'columns', 'option_name' => \__('Toggle Columns', 'independent-analytics'), 'option_icon' => 'columns', 'plugin_groups' => Plugin_Group::get_plugin_groups(), 'options' => $this->get_columns(\true)]);
    }
    public function get_table_toolbar_markup()
    {
        return \IAWPSCOPED\iawp_blade()->run('tables.table-toolbar', ['plugin_groups' => Plugin_Group::get_plugin_groups(), 'columns' => $this->get_columns(\true), 'groups' => $this->groups(), 'current_group' => $this->group()]);
    }
    public function get_table_markup(string $sort_column, string $sort_direction)
    {
        return \IAWPSCOPED\iawp_blade()->run('tables.table', ['table' => $this, 'all_columns' => $this->get_columns(), 'visible_column_count' => $this->visible_column_count(), 'number_of_shown_rows' => 0, 'rows' => [], 'render_skeleton' => \true, 'page_size' => \IAWPSCOPED\iawp()->pagination_page_size(), 'sort_column' => $sort_column, 'sort_direction' => $sort_direction, 'has_campaigns' => Campaign_Builder::has_campaigns()]);
    }
    public function set_statistics(Statistics $statistics)
    {
        $this->statistics = $statistics;
    }
    public function get_row_data_attributes($row)
    {
        $html = '';
        foreach ($this->get_columns() as $column) {
            $id = $column->id();
            $data_val = $row->{$id}();
            $html .= ' data-' . \esc_attr($column->id()) . '="' . \esc_attr($data_val) . '"';
        }
        return $html;
    }
    public function get_cell_content($row, Column $column)
    {
        $column_id = $column->id();
        if (\is_null($row->{$column_id}())) {
            return '-';
        }
        if ($column_id == 'title' && $row->is_deleted()) {
            return Security::string($row->{$column_id}()) . ' <span class="deleted-label">' . \esc_html__('(deleted)', 'independent-analytics') . '</span>';
        } elseif ($column_id == 'views') {
            $views = Number_Formatter::decimal($row->views());
            // Getting a divide by zero error from the line below?
            // It's likely an issue with $this->views which is an instance of Views. Make sure the queries there are working.
            $views_percentage = Number_Formatter::percent($row->views() / $this->statistics->get_statistic('views')->value() * 100, 2);
            return '<span class="no-wrap">' . Security::string($views) . '</span> <span class="percentage">(' . Security::string($views_percentage) . ')</span>';
        } elseif ($column_id == 'visitors') {
            $visitors = Number_Formatter::decimal($row->visitors());
            $visitors_percentage = Number_Formatter::percent($row->visitors() / $this->statistics->get_statistic('visitors')->value() * 100, 2);
            return '<span class="no-wrap">' . Security::string($visitors) . '</span> <span class="percentage">(' . Security::string($visitors_percentage) . ')</span>';
        } elseif ($column_id == 'sessions') {
            $sessions = Number_Formatter::decimal($row->sessions());
            $sessions_percentage = Number_Formatter::percent($row->sessions() / $this->statistics->get_statistic('sessions')->value() * 100, 2);
            return '<span class="no-wrap">' . Security::string($sessions) . '</span> <span class="percentage">(' . Security::string($sessions_percentage) . ')</span>';
        } elseif ($column_id === 'entrances') {
            $entrances = Number_Formatter::decimal($row->entrances());
            $entrances_percentage = Number_Formatter::percent($row->entrances() / $this->statistics->get_statistic('sessions')->value() * 100, 2);
            return '<span class="no-wrap">' . Security::string($entrances) . '</span> <span class="percentage">(' . Security::string($entrances_percentage) . ')</span>';
        } elseif ($column_id === 'exits') {
            $exits = Number_Formatter::decimal($row->exits());
            $exits_percentage = Number_Formatter::percent($row->exits() / $this->statistics->get_statistic('sessions')->value() * 100, 2);
            return '<span class="no-wrap">' . Security::string($exits) . '</span> <span class="percentage">(' . Security::string($exits_percentage) . ')</span>';
        } elseif ($column_id === 'bounce_rate') {
            return Security::string(Number_Formatter::percent($row->bounce_rate()));
        } elseif ($column_id === 'average_session_duration' || $column_id === 'average_view_duration') {
            return Number_Formatter::second_to_minute_timestamp($row->{$column_id}());
        } elseif ($column_id === 'views_growth' || $column_id === 'visitors_growth' || $column_id === 'wc_conversion_rate' || $column_id === 'exit_percent' || Str::startsWith($column_id, 'form_conversion_rate')) {
            return Number_Formatter::percent($row->{$column_id}(), 2);
        } elseif ($column_id == 'url') {
            if ($row->is_deleted()) {
                return \urldecode(\esc_url($row->url()));
            } else {
                return '<a href="' . \esc_url($row->url(\true)) . '" target="_blank" class="external-link">' . \urldecode(\esc_url($row->url())) . '<span class="dashicons dashicons-external"></span></a>';
            }
        } elseif ($column_id == 'author') {
            return Security::html($row->avatar()) . ' ' . Security::string($row->author());
        } elseif ($column_id == 'date') {
            return Security::string(\date(WordPress_Site_Date_Format_Pattern::for_php(), \strtotime($row->date())));
        } elseif ($column_id == 'type' && \method_exists($row, 'icon') && \method_exists($row, 'type')) {
            return $row->icon(0) . ' ' . Security::string($row->type());
        } elseif ($column_id == 'referrer') {
            return \IAWPSCOPED\iawp_render('tables.referrer-column', ['row' => $row]);
        } elseif ($column_id === 'device_type') {
            return Icon_Directory_Factory::device_types()->find($row->device_type()) . Security::string($row->device_type());
        } elseif ($column_id === 'browser') {
            return Icon_Directory_Factory::browsers()->find($row->browser()) . Security::string($row->browser());
        } elseif ($column_id === 'os') {
            return Icon_Directory_Factory::operating_systems()->find($row->os()) . Security::string($row->os());
        } elseif ($column_id === 'country') {
            return Icon_Directory_Factory::flags()->find($row->country_code()) . Security::string($row->country());
        } elseif ($column_id === 'wc_gross_sales' || $column_id === 'wc_refunded_amount' || $column_id === 'wc_net_sales' || $column_id === 'wc_average_order_volume') {
            return Security::string(Currency::format($row->{$column_id}()));
        } elseif ($column_id === 'wc_earnings_per_visitor') {
            return Security::string(Currency::format($row->{$column_id}(), \false));
        } elseif ($column_id === 'views_per_session') {
            return Number_Formatter::decimal($row->{$column_id}(), 2);
        } elseif ($column_id === 'link_target') {
            $value = $row->{$column_id}();
            if (\is_string($value) && URL::new($value)->is_valid_url()) {
                return '<a href="' . \esc_url($value) . '" target="_blank" class="external-link">' . \esc_url(\urldecode($value)) . '<span class="dashicons dashicons-external"></span></a>';
            }
            return Security::string($value);
        } else {
            return Security::string($row->{$column_id}());
        }
    }
    public function output_report_toolbar()
    {
        $options = Dashboard_Options::getInstance();
        $start = $options->get_date_range()->start()->setTimezone(Timezone::site_timezone());
        $end = $options->get_date_range()->end()->setTimezone(Timezone::site_timezone());
        ?>
        <div id="toolbar" class="toolbar" data-filter-count="<?php 
        echo \count($options->filters());
        ?>">
        <div class="date-picker-parent">
            <div class="modal-parent dates">
                <button id="dates-button"
                        data-testid="open-calendar"
                        class="iawp-button"
                        data-action="dates#toggleModal"
                        data-dates-target="modalButton"
                >
                    <span class="dashicons dashicons-calendar-alt"></span>
                    <span class="iawp-label"><?php 
        echo \esc_html($options->get_date_range()->label());
        ?></span>
                </button>
                <div id="modal-dates"
                     class="iawp-modal large dates"
                     data-dates-target="modal"
                >
                    <?php 
        echo (new Date_Picker($start, $end, $options->relative_range_id()))->calendar_html();
        ?>
                </div>
            </div>
        </div>
        <div class="filter-parent">
            <?php 
        echo $this->filters()->get_filters_html($this->columns_that_can_be_filtered());
        ?>
        </div>
        <div class="download-options-parent <?php 
        echo $this->allow_downloading() ? "" : "hide";
        ?>" data-controller="modal">
            <div class="modal-parent downloads">
                <button id="download-options" data-modal-target="modalButton" data-action="click->modal#toggleModal" class="download-options">
                    <?php 
        \esc_html_e('Download Report', 'independent-analytics');
        ?>
                </button>
                <div class="iawp-modal small downloads" data-modal-target="modal">
                    <div class="modal-inner">
                        <div class="title-small">
                            <?php 
        \esc_html_e('Choose a format', 'independent-analytics');
        ?>
                            <span data-report-target="spinner" class="dashicons dashicons-update iawp-spin hidden"></span>
                        </div>
                        <div class="download-button-container">
                            <button id="download-csv" class="iawp-button" data-report-target="exportReportTable" data-action="report#exportReportTable">
                                <span class="dashicons dashicons-media-spreadsheet"></span>
                                <span class="iawp-label">
                                    <?php 
        \esc_html_e('Download Table CSV', 'independent-analytics');
        ?>
                                </span>
                            </button>
                            <button id="download-report-statistics-csv" class="iawp-button" data-report-target="exportReportStatistics" data-action="report#exportReportStatistics">
                                <span class="dashicons dashicons-media-spreadsheet"></span>
                                <span class="iawp-label">
                                    <?php 
        \esc_html_e('Download Daily Metrics CSV', 'independent-analytics');
        ?>
                                </span>
                            </button>
                        </div>
                        <div class="download-button-container">
                            <button id="download-pdf" class="iawp-button" data-report-target="exportPDF" data-action="report#exportPDF" disabled="disabled">
                                <span class="dashicons dashicons-pdf"></span>
                                <span class="iawp-label">
                                    <?php 
        \esc_html_e('Download PDF', 'independent-analytics');
        ?>
                                </span>
                            </button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        </div><?php 
    }
    public function filters_template_html() : string
    {
        return $this->filters()->get_condition_html($this->columns_that_can_be_filtered());
    }
    public function filters_condition_buttons_html(array $filters) : string
    {
        return $this->filters()->condition_buttons_html($filters);
    }
    public final function csv(array $rows, bool $is_dashboard_export = \false) : CSV
    {
        $columns = $this->get_columns();
        $csv_header = [];
        $csv_rows = [];
        foreach ($columns as $column) {
            if (!$this->include_column_in_csv($column, $is_dashboard_export)) {
                continue;
            }
            $csv_header[] = $column->name();
        }
        foreach ($rows as $row) {
            $csv_row = [];
            foreach ($columns as $column) {
                if (!$this->include_column_in_csv($column, $is_dashboard_export)) {
                    continue;
                }
                $column_id = $column->id();
                $value = $row->{$column_id}();
                if (\is_string($value)) {
                    $value = \html_entity_decode($value);
                    // Fix apostrophes for Excel
                    $value = \str_replace("’", "'", $value);
                }
                $csv_row[] = $this->formatted_csv_cell_content($column, $value);
            }
            $csv_rows[] = $csv_row;
        }
        $csv = new CSV($csv_header, $csv_rows);
        return $csv;
    }
    public function formatted_csv_cell_content(Column $column, $value) : string
    {
        $column_id = $column->id();
        // Todo - This logic is similar to the rendering logic for table cells. This should
        //  all be handled via the column class itself.
        if (\is_null($value)) {
            return '-';
        } elseif (\in_array($column_id, ['views', 'visitors', 'sessions', 'clicks', 'form_submissions']) || Str::startsWith($column_id, 'form_submissions_for_')) {
            return Number_Formatter::integer($value);
        } elseif (\in_array($column_id, ['bounce_route', 'views_growth', 'visitors_growth', 'form_conversion_rate']) || Str::startsWith($column_id, 'form_conversion_rate_for_')) {
            return Number_Formatter::percent($value);
        } elseif ($column_id === 'date') {
            return \date(WordPress_Site_Date_Format_Pattern::for_php(), \strtotime($value));
        } elseif ($column_id === 'average_session_duration' || $column_id === 'average_view_duration') {
            return Number_Formatter::second_to_minute_timestamp($value);
        } elseif ($column_id === 'views_per_session') {
            return Number_Formatter::decimal($value, 2);
        } elseif ($column_id === 'wc_gross_sales' || $column_id === 'wc_refunded_amount' || $column_id === 'wc_net_sales' || $column_id === 'wc_average_order_volume') {
            return Currency::format($value);
        } elseif ($column_id === 'wc_earnings_per_visitor') {
            return Currency::format($value);
        } else {
            return $value;
        }
    }
    /**
     * @param array[] $filters Raw filter associative arrays
     *
     * @return Filter[]
     */
    public function sanitize_filters(array $filters) : array
    {
        return Collection::make($filters)->map(function ($filter) {
            return $this->sanitize_filter($filter);
        })->filter()->values()->all();
    }
    public function resolve_filter_conflicts(array $filters, string $filter_logic) : array
    {
        return $filters;
    }
    public function sanitize_filter(array $filter) : ?Filter
    {
        // column, inclusion, operator, operand
        $column = $this->get_column($filter['column']);
        if ($column === null || !$column->can_be_filtered()) {
            return null;
        }
        $valid_inclusions = ['include', 'exclude'];
        if (!\in_array($filter['inclusion'], $valid_inclusions)) {
            return null;
        }
        if (!$column->is_valid_filter_operator($filter['operator'])) {
            return null;
        }
        if (!$column->is_enabled_for_group($this->group())) {
            return null;
        }
        $operand = \trim(Security::string($filter['operand']));
        if (\strlen($operand) === 0) {
            return null;
        }
        if ($column->id() === 'url' && $filter['operator'] === 'exact') {
            $url = new URL($filter['operand']);
            if (!$url->is_valid_url()) {
                $filter['operand'] = \site_url($filter['operand']);
            }
        }
        if ($column->options() && !$column->options()->contains($filter['operand'])) {
            return null;
        }
        return new Filter(['inclusion' => Security::string($filter['inclusion']), 'operator' => Security::string($filter['operator']), 'operand' => Security::string($filter['operand']), 'column' => $column]);
    }
    public function get_column(string $id) : ?Column
    {
        $matches = \array_filter($this->local_columns(), function (Column $column) use($id) {
            return $column->id() === $id;
        });
        $column = \count($matches) === 1 ? \reset($matches) : null;
        if (\is_null($column) || !$column->is_enabled()) {
            return null;
        }
        return $column;
    }
    public function sanitize_sort_parameters(?string $sort_column = null, ?string $sort_direction = 'desc') : Sort_Configuration
    {
        if ($sort_column === null) {
            $sort_column = $this->default_sorting_column;
        }
        $column = $this->get_column($sort_column);
        if ($column === null || !$column->is_enabled_for_group($this->group)) {
            $column = $this->get_column($this->default_sorting_column);
        }
        return new Sort_Configuration($column, $sort_direction);
    }
    public function get_rendered_template(array $rows, bool $just_rows, string $sort_column, string $sort_direction)
    {
        if ($just_rows) {
            return \IAWPSCOPED\iawp_blade()->run('tables.rows', ['table' => $this, 'all_columns' => $this->get_columns(), 'visible_column_count' => $this->visible_column_count(), 'number_of_shown_rows' => \count($rows), 'rows' => $rows, 'render_skeleton' => \false, 'page_size' => \IAWPSCOPED\iawp()->pagination_page_size(), 'sort_column' => $sort_column, 'sort_direction' => $sort_direction, 'has_campaigns' => Campaign_Builder::has_campaigns()]);
        }
        return \IAWPSCOPED\iawp_blade()->run('tables.table', ['table' => $this, 'all_columns' => $this->get_columns(), 'visible_column_count' => $this->visible_column_count(), 'number_of_shown_rows' => \count($rows), 'rows' => $rows, 'render_skeleton' => \false, 'page_size' => \IAWPSCOPED\iawp()->pagination_page_size(), 'sort_column' => $sort_column, 'sort_direction' => $sort_direction, 'has_campaigns' => Campaign_Builder::has_campaigns()]);
    }
    /**
     * @return Column[]
     */
    public function get_columns($show_disabled_columns = \false) : array
    {
        $columns_for_group = \array_filter($this->local_columns(), function (Column $column) {
            return $column->is_enabled() && $column->is_enabled_for_group($this->group) && $column->is_subgroup_plugin_enabled();
        });
        if (\false === $show_disabled_columns) {
            $columns_for_group = \array_filter($columns_for_group, function (Column $column) {
                return $column->is_group_plugin_enabled();
            });
        }
        if (\is_null($this->visible_columns) || \count($this->visible_columns) === 0) {
            return $columns_for_group;
        }
        if (!$this->is_new_group) {
            return \array_map(function ($column) {
                $column->set_visibility(\in_array($column->id(), $this->visible_columns));
                return $column;
            }, $columns_for_group);
        }
        return \array_map(function ($column) {
            if ($column->is_group_dependent()) {
                $column->set_visibility(\true);
            } elseif (!\in_array($column->id(), $this->visible_columns)) {
                $column->set_visibility(\false);
            }
            return $column;
        }, $columns_for_group);
    }
    protected function get_woocommerce_columns() : array
    {
        return [new Column(['id' => 'wc_orders', 'name' => \__('Orders', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'type' => 'int', 'aggregatable' => \true]), new Column(['id' => 'wc_gross_sales', 'name' => \__('Gross Sales', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'type' => 'int', 'aggregatable' => \true]), new Column(['id' => 'wc_refunds', 'name' => \__('Refunds', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'type' => 'int', 'aggregatable' => \true]), new Column(['id' => 'wc_refunded_amount', 'name' => \__('Refunded Amount', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'type' => 'int', 'aggregatable' => \true]), new Column(['id' => 'wc_net_sales', 'name' => \__('Total Sales', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'type' => 'int', 'aggregatable' => \true]), new Column(['id' => 'wc_conversion_rate', 'name' => \__('Conversion Rate', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'type' => 'int']), new Column(['id' => 'wc_earnings_per_visitor', 'name' => \__('Earnings Per Visitor', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'type' => 'int']), new Column(['id' => 'wc_average_order_volume', 'name' => \__('Average Order Volume', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'type' => 'int'])];
    }
    protected function get_form_columns() : array
    {
        $columns = [new Column(['id' => 'form_submissions', 'name' => \__('Submissions', 'independent-analytics'), 'plugin_group' => 'forms', 'type' => 'int', 'aggregatable' => \true]), new Column(['id' => 'form_conversion_rate', 'name' => \__('Conversion Rate', 'independent-analytics'), 'plugin_group' => 'forms', 'type' => 'int'])];
        foreach (Form::get_forms() as $form) {
            $columns[] = new Column(['id' => $form->submissions_column(), 'name' => $form->title() . ' ' . \__('Submissions', 'independent-analytics'), 'plugin_group' => 'forms', 'is_subgroup_plugin_active' => $form->is_plugin_active(), 'plugin_group_header' => $form->plugin_name(), 'type' => 'int', 'aggregatable' => \true]);
            $columns[] = new Column(['id' => $form->conversion_rate_column(), 'name' => $form->title() . ' ' . \__('Conversion Rate', 'independent-analytics'), 'plugin_group' => 'forms', 'is_subgroup_plugin_active' => $form->is_plugin_active(), 'plugin_group_header' => $form->plugin_name(), 'type' => 'int']);
        }
        return $columns;
    }
    private function include_column_in_csv(Column $column, bool $is_dashboard_export) : bool
    {
        if (!$column->is_visible() && $is_dashboard_export) {
            return \false;
        }
        if (!$column->exportable() && !$is_dashboard_export) {
            return \false;
        }
        if (!$column->is_group_plugin_enabled()) {
            return \false;
        }
        return \true;
    }
    /**
     * Get the number of visible columns
     *
     * @return int
     */
    private function visible_column_count() : int
    {
        $visible_columns = 0;
        foreach ($this->get_columns() as $column) {
            if ($column->is_visible()) {
                $visible_columns++;
            }
        }
        return $visible_columns;
    }
    private function filters()
    {
        return $this->filters;
    }
    private function columns_that_can_be_filtered() : array
    {
        return Collection::make($this->get_columns())->filter(fn(Column $column) => $column->can_be_filtered())->all();
    }
}
